#include "updater.h"
#include "conf.h"
#include "image_loader.h"
#include "apm32a10x_fmc.h"
#include "stdio.h"
#include "fatfs/ff.h"

/* 库文件 */
#include "apm32a10x_gpio.h"
static GPIO_T *beeper_port = GPIOB;
static GPIO_PIN_T beeper_pin = GPIO_PIN_8;
unsigned int Updater_eraseFlash(void *address,unsigned int size){
    volatile FMC_STATUS_T erase_status = FMC_STATUS_COMPLETE;
    int offset = (uint32_t)address % 2048;
    int erase_size = size + offset;
    int erase_address = ((uint32_t)address) - offset;
    int npage = ((erase_size % 2048) == 0) ? (erase_size / 2048) : (erase_size / 2048 + 1);
    for ( int page = 0; page < npage; page++ ){
        /* Erase page. */
        erase_status = FMC_ErasePage(erase_address);
        if ( erase_status != FMC_STATUS_COMPLETE ){
            printf("Error occured\r\n");
            switch ( erase_status ){
                case FMC_STATUS_BUSY: printf("Error occured:BUSY\r\n"); break;
                case FMC_STATUS_ERROR_PG: printf("Error occured:ERROR PG\r\n"); break;
                case FMC_STATUS_ERROR_WRP: printf("Error occured:ERROR WRP\r\n"); break;
                case FMC_STATUS_TIMEOUT: printf("Error occured:TIMEOUT\r\n"); break;
            }
            break;
        }
        erase_address += 2048;
    }
    while ( FMC->STS_B.BUSYF );
    return size;
}

unsigned int Updater_copyFlash(void *dest,void *src,unsigned int size) {
    volatile uint32_t dest_hw = (uint32_t)dest;
    volatile uint32_t src_hw = (uint32_t)src;
    uint32_t nhw = (size % 2 == 0) ? (size / 2) : (size / 2 + 1);   /* Half words need to be copy. */
    volatile FMC_STATUS_T flash_status = FMC_STATUS_COMPLETE;
    uint16_t data = 0;
    
    for ( int nword = 0; nword < nhw; nword++ ){
        data = *(uint16_t*)src_hw;
        flash_status = FMC_ProgramHalfWord(dest_hw,data);
        if ( flash_status != FMC_STATUS_COMPLETE ){
            printf("Error occured\r\n");
            switch ( flash_status ){
                case FMC_STATUS_BUSY: printf("Error occured:BUSY\r\n"); break;
                case FMC_STATUS_ERROR_PG: printf("Error occured:ERROR PG\r\n"); break;
                case FMC_STATUS_ERROR_WRP: printf("Error occured:ERROR WRP\r\n"); break;
                case FMC_STATUS_TIMEOUT: printf("Error occured:TIMEOUT\r\n"); break;
            }
            //break;

            return 0;
        }
        src_hw += 2;
        dest_hw += 2;
    }
    return size;
}

int writeOTAData(unsigned int address_offset,void *buffer,unsigned int size){
    char * rec_buffer = (char *)buffer;
    unsigned int ret = 0;
    uint32_t ota_base_address = OTA_AREA_ADDRESS;
    uint32_t target_address = ota_base_address + address_offset;
    // printf("Write from %p to 0x%.8x size %d\r\n",buffer,address_offset,size);
    /*检查地址范围是否正确 check the address*/
    if(target_address < ota_base_address ||  \
       target_address + size > ota_base_address + (OTA_AREA_ADDRESS - APP_AREA_ADDRESS)){
        return -1;
    }
    printf("target_address size %x %d\r\n",target_address,size);

    if( (target_address % 2048) == 0 ){
    FMC_Unlock();
    ret=Updater_eraseFlash(target_address,size);
    if(ret==size)printf("erase success\r\n");
    else return 1;
    FMC_Lock();
    }

    FMC_Unlock();
    ret=Updater_copyFlash(target_address,buffer,size);
    if(ret==size)printf("copyFlash success\r\n");
    else return 1;
    FMC_Lock();
    return 0;
}

unsigned int Updater_checkSDCard_v2(){
    uint32_t target_address = OTA_AREA_ADDRESS;
    /* 文件系统相关变量 */
    FATFS fs;
    FRESULT res;
    FIL fp;    
    FILINFO fno;

    int ret;
    unsigned int br,file_size;
    unsigned int byte_read;
    unsigned int btr = 512;   
    char read_buffer[512]={0}; 
    static unsigned int ota_address_offset = 0x00000000;
    int sd_ret = 1;

    /* 挂载 */
    res = f_mount(&fs,"0:",1);
    if ( res != FR_OK ){
        printf("mount error:%d\r\n",res);
        return 1;
    }else{
        printf("mount success\r\n");
    }


    /* 检测有无升级成功文件 */
    res = f_stat("0:/update.bin.success.block", &fno);
    if( res == FR_OK){
        printf("exist\r\n");
        return 1;
    }
    res = f_open(&fp,"0:/update.bin",FA_READ | FA_WRITE);
    if ( res != FR_OK ){
        printf("open error\r\n");
        return 1;
    }
    else{
        /* 检测到升级文件 */
        GPIO_SetBit(beeper_port,beeper_pin);
        System_delayMs(500);
        GPIO_ResetBit(beeper_port,beeper_pin);
        printf("open suuccess\r\n");
        file_size=f_size(&fp);
        printf("file size %d\r\n",file_size); 

        /* 擦除Flash */
        FMC_Unlock();
        ret = Updater_eraseFlash((void*)target_address,file_size);
        if(ret==file_size)printf("erase success\r\n");
        else return 1;
        FMC_Lock();

        do{
            res = f_read(&fp,read_buffer,btr,&br);
            byte_read = br;
            if(res != FR_OK){
                f_sync(&fp);
                // res= f_open(&fp,"0:/update.bin.success.block", FA_WRITE | FA_CREATE_NEW);
                res= f_open(&fp,"0:/update.bin.error.log", FA_WRITE | FA_CREATE_NEW);
                printf("read failed\r\n");
                return 1;
            }
            sd_ret=writeSDData(ota_address_offset,read_buffer,br);
            
            /* SD卡写入flash失败*/
            if( sd_ret ==1 ){
                res= f_open(&fp,"0:/update.bin.error.log", FA_WRITE | FA_CREATE_NEW);
                printf("write failed");
                return 1;
            }

            ota_address_offset += br;
            //printf("\r\nread size %d\r\n",byte_read);       
            }while(br==btr);
            
            f_sync(&fp);
            res= f_open(&fp,"0:/update.bin.success.block", FA_WRITE | FA_CREATE_NEW);
            if( res != FR_OK){
                printf("新建失败 %d\r\n",res);
                // f_unlink("0:/update.bin");
            }
            f_close(&fp);

        }
    f_unmount("0:");
    printf("OTA data copy finished. reboot.\r\n");
    NVIC_SystemReset();
    return 0;
}


unsigned int Updater_checkSDCard(){

    FATFS fs;
    FRESULT res;
    FIL fp;    
    FILINFO fno;

    unsigned int br,file_size;
    unsigned int byte_read;
    unsigned int btr = 512;   
    char read_buffer[512]={0}; 
    static unsigned int ota_address_offset = 0x00000000;
    int sd_ret = 1;
    res = f_mount(&fs,"0:",1);
    if ( res != FR_OK ){
        printf("mount error:%d\r\n",res);
        return 1;
    }
    else{
        printf("mount success\r\n");
    }

    res = f_stat("0:/update.bin.success.block", &fno);
    if( res == FR_OK){
        printf("exist\r\n");
        return 1;
    }
    res = f_open(&fp,"0:/update.bin",FA_READ | FA_WRITE);
    if ( res != FR_OK ){
        printf("open error\r\n");
        return 1;
    }
    else{
        /* 检测到升级文件 */
        GPIO_SetBit(beeper_port,beeper_pin);
        System_delayMs(500);
        GPIO_ResetBit(beeper_port,beeper_pin);
        
        printf("open suuccess\r\n");
        file_size=f_size(&fp);
        printf("file size %d\r\n",file_size); 
        do{
            res = f_read(&fp,read_buffer,btr,&br);
            byte_read = br;
            if(res != FR_OK){
                res= f_open(&fp,"0:/update.bin.error.log", FA_WRITE | FA_CREATE_NEW);
                printf("read failed\r\n");
                return 1;
            }
            sd_ret=writeOTAData(ota_address_offset,read_buffer,br);
            ota_address_offset += br;
            //printf("\r\nread size %d\r\n",byte_read);       
            }while(br==btr);
            
            f_sync(&fp);

            res= f_open(&fp,"0:/update.bin.success.block", FA_WRITE | FA_CREATE_NEW);
            if( res != FR_OK){

                f_unlink("0:/update.bin");
            }
            f_close(&fp);

        }
    f_unmount("0:");
    printf("OTA data copy finished. reboot.\r\n");
    NVIC_SystemReset();
    return 0;
}

int writeSDData(unsigned int address_offset,void *buffer,unsigned int size){
    char * rec_buffer = (char *)buffer;
    unsigned int ret = 0;
    uint32_t ota_base_address = OTA_AREA_ADDRESS;
    uint32_t target_address = ota_base_address + address_offset;
    // printf("Write from %p to 0x%.8x size %d\r\n",buffer,address_offset,size);
    /*检查地址范围是否正确 check the address*/
    // if(target_address < ota_base_address ||  \
    //    target_address + size > ota_base_address + (OTA_AREA_ADDRESS - APP_AREA_ADDRESS)){
    //     return -1;
    // }
    // printf("target_address size %x %d\r\n",target_address,size);

    // if( (target_address % 2048) == 0 ){
    // FMC_Unlock();
    // ret=Updater_eraseFlash(target_address,size);
    // if(ret==size)printf("erase success\r\n");
    // else return 1;
    // FMC_Lock();
    // }
    
    FMC_Unlock();
    ret=Updater_copyFlash(target_address,rec_buffer,size);
    if(ret==size)printf("copyFlash success\r\n");
    else return 1;
    FMC_Lock();

    return 0;
}

int ble_writeOTAData(unsigned int address_offset,void *buffer,unsigned int size){
    char * rec_buffer = (char *)buffer;
    unsigned int ret = 0;
    uint32_t ota_base_address = OTA_AREA_ADDRESS;
    uint32_t target_address = ota_base_address + address_offset;
    // printf("Write from %p to 0x%.8x size %d\r\n",buffer,address_offset,size);
    /*检查地址范围是否正确 check the address*/
    // if(target_address < ota_base_address ||  \
    //    target_address + size > ota_base_address + (OTA_AREA_ADDRESS - APP_AREA_ADDRESS)){
    //     return -1;
    // }
    printf("target_address size %x %d\r\n",target_address,size);

    // if( (target_address % 2048) == 0 ){
    // FMC_Unlock();
    // ret=Updater_eraseFlash(target_address,size);
    // if(ret==size)printf("erase success\r\n");
    // else return 1;
    // FMC_Lock();
    // }
    
    FMC_Unlock();
    ret=Updater_copyFlash(target_address,rec_buffer,size);
    if(ret==size)printf("copyFlash success\r\n");
    else return 1;
    FMC_Lock();

    return 0;
}